#ifndef __I_UNIX_NET_H__
#define __I_UNIX_NET_H__

#include <sys/epoll.h>
#include <string.h>
#include <zlib/eventsystem/I_EventSystem.h>
#include <zlib/lib/Ink_inet.h>
#include <unistd.h>

#define EVENTIO_READ	(EPOLLIN  | EPOLLET)
#define EVENTIO_WRITE	(EPOLLOUT | EPOLLET)

//It's not a real error
//In this case we will continue read or write fd
//if read or write error, we think fd bad.
#define EVENTIO_ERROR	(EPOLLERR | EPOLLPRI | EPOLLHUP)

//the epoll_wait max listen events
#define POLL_DESCRIPTOR_SIZE 32768

#define POLL_TIMEOUT_MSECOND 5

class UnixNetVConnect;

struct PollDescriptor
{
	int result; 			//events number
	int epoll_fd;			//by epoll_create create fd
	struct epoll_event epoll_Trigger_Events[POLL_DESCRIPTOR_SIZE];

	int get_epollfd();
	uint32_t get_ev_events(int index);
	void *get_ev_data(int index);

	PollDescriptor();
	~PollDescriptor()
	{
		close(epoll_fd);
	}
};

enum IOType{
	EVENT_IO_NONE = 0,
	EVENT_IO_READWRITE,
	EVENT_ASYNC_SIGNAL
};

struct EventIO
{
	int fd;
	uint32_t events;
	//the pd->epoll_fd manager this fd
	PollDescriptor *event_loop;
	//the fd type (read and write fd, accept fd, signal fd, or other fd)
	IOType type;
	Continuation *cont;

	int start(PollDescriptor *pd, UnixNetVConnect *vc, uint32_t e);
	int start(PollDescriptor *pd, int afd, Continuation *c, uint32_t e);
	//add remove and modify, but not overwrite
	int modify(uint32_t e, bool add = true);
	int stop();

	EventIO()
	{
		type = EVENT_IO_NONE;
		cont = NULL;
		event_loop = NULL;
	}
};

struct PollPoller
{
	PollDescriptor *pd;
	//default poll timeout
	int poll_timeout;

	void pollEvent(int wait_timeout = 0);

	//the epoll_wait timeout is msecond
	PollPoller(int p_timeout = POLL_TIMEOUT_MSECOND);
	~PollPoller()
	{
		assert(pd != NULL);
		delete pd;
	}
};

struct NetOptions
{
	//IPv4 or IPv6
	int _family;
	//TCP or UDP
	int _sock_type;

	bool _bind_addr;
	//bind local addr
	IpEntry local;

	bool fd_blocking;
	bool fd_blocking_connect;

	NetOptions();
};

struct Connection
{
	//socket for connection
	int fd;
	IpEntry _remote_entry;
	//flag for already bind.
	bool is_bind;
	//flag for already connected
	bool is_connected;

	//return sockfd.
	int open(NetOptions opt = default_opt);
	int connect(IpEntry *ip_entry, NetOptions opt = default_opt);
	void close();

	Connection();
	static const NetOptions default_opt;
};

struct AcceptOptions
{
	bool blocking_accept;
	bool fd_blocking;
	int sock_type;	//tcp or udp

	AcceptOptions();
};

//the server not allow call open and connect function
struct Server : public Connection
{
	bool is_listen;
	IpEntry bind_entry;

	int listen(const IpEntry *abind_entry, int sock_type, bool fd_blocking_accept = true);
	int accept(Connection *conn, bool fd_blocking = false);
	Server();
};

#endif //__I_UNIX_NET_H__
